package com.cci.kangdao.aop;

import com.cci.kangdao.common.RedisKeys;
import com.cci.kangdao.dao.AKeyLoginUserTokenDao;
import com.cci.kangdao.dao.LoginDao;
import com.cci.kangdao.dao.UserTokenTMapper;
import com.cci.kangdao.dao.model.AKeyLoginUserTokenT;
import com.cci.kangdao.dao.model.UserT;
import com.cci.kangdao.dao.model.UserTokenT;
import com.cci.kangdao.redis.RedisClientUtils;
import com.cci.kangdao.service.PropertiesUtils;
import com.cci.kangdao.utilTool.*;
import com.cci.kangdao.wechatOrder.model.UserOutSideTokenT;
import com.cci.kangdao.wechatOrder.service.IOutSideTokenService;
import com.github.pagehelper.PageHelper;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@Component
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
public class UserLoginIntercept {

    //不验证权限接口
    private static final String URI_STR = "test.aspx,getGoOutTypeList.aspx,fileUpload.aspx,getApiHost.aspx,getBillNo.aspx,getFeedBackMD.aspx,submitFeedbackV4.aspx,submitFeedbackV5.aspx,getClientId.aspx,getMobileSms.aspx,verifyMobile.aspx,userRegister.aspx,login.aspx,verifyToken.aspx,"
            + "digitalQuery.aspx,getDictionaries.aspx,searchServiceStation.aspx,searchFaultCode.aspx,searchFaultCode2.aspx,delRediskey.aspx,submitFeedback.aspx,"
            + "uploadRecordLogs.aspx,getAppVersion.aspx,getIntegralSetup.aspx,authCheck.aspx.getBaseInfo.aspx,getEngsByCom.aspx,getStations.aspx,crmApi.aspx,"
            + "getLoginUrl.aspx,checkOrganization.aspx,initInfo.aspx,register.aspx,findPassword.aspx,getOrganizationList.aspx,getWeChatGetMaterialList.aspx,silentLogin.aspx,getCommonQuestionTypeList.aspx,getCommonQuestionDetailList.aspx,"
            + "getDictionaryList.aspx,retryExceptionOrder.aspx,syncServiceTechnicianMsgToCRMTask.aspx,adminApproveRoles.aspx,activity.aspx,"
            + "syncCcgServiceItem.aspx,syncCcgTemplate.aspx,syncCcgStandard.aspx,syncCcgEngineers.aspx,syncCcgWorkOrderInfo.aspx,syncCCGWorkOrderStatus.aspx,syncCCGEvaluationInfo.aspx,retryFieldServiceExceptionSync.aspx"
            + "file/upload.aspx,getServiceStationV2.aspx,getMetadataForServiceStation.aspx,getTrackPages.aspx,getCodeByEmail.aspx,verifyEmailCode.aspx,getReasonList.aspx,getUser.aspx,getUserInfoByMT.aspx,,sendVerifyCodeByPhoneCSU.aspx,getMTMsgByWorkOrder.aspx,sendVerifyCodeByPhoneCSU.aspx,"
            + "checkVerifyCodePhoneStatusCSU.aspx,getUserInfoByESNCSU.aspx,ew/getAuthInfo.aspx,getEngineTypeList.aspx,mpUpdateUserPassword.aspx,syncFaultLocationToCRM.aspx"
            + "userCheck.aspx,outsideUserRegister.aspx,getOpenId.aspx,analysisToken.aspx,uploadLocation.aspx,outSideUserGetOrderInfoById.aspx,checkPhoneBeforeRegistWX.aspx,workOrderClosedAbnormally.aspx,getCompanysGuidanz.aspx,setLocationRedis.aspx,getWorkCardListDetail.aspx,"
            + "/outside/getPictures.aspx,/checkDCECUserRoleExist.aspx,sendSms.aspx,set.aspx,send.aspx,/openActivity.aspx,/appGet.aspx,getDictionaryListByDTableName.aspx,getLicensePlateNumberByEsn.aspx,aKeyLogin.aspx,refreshLocalToken.aspx,aKeyLoginSendVerifyCode.aspx,aKeyLoginVerifyIdentity.aspx,getFCCodeBySPNAndFMI.aspx,getSMN.aspx"
            + "user/delete.aspx,outSystemSendSms.aspx,/cipFleet/getFleetByUserId.aspx,/cipFleet/insertFleet.aspx,/cipFleet/isFleetWhite.aspx,/cipFleet/deleteFleet.aspx,motest.aspx,/phone.aspx,/test.aspx,/batchUpdateEsnAppPowerType.aspx,/syncEsnAndUser/syncByUserId.aspx,retryExceptionDataSyncCrm.aspx,syncNpbuEngineTypeToEApp.aspx,/weChatLoginOut.aspx,"
            + "/getRedisToTable.aspx,/workOrderTask.aspx,/workOrderSynchronization.aspx,/campaignTaskRetry.aspx,/taskCycle.aspx,/updateEsnAppPowerType.aspx,/synchronizationWeChatData.aspx,/dealFeedBack.aspx,/serviceAgreement.aspx,/escalationOrderTask.aspx,/syncESOrderDetail.aspx,/exceptionDataSyncCrm.aspx,/syncRetry.aspx,/overTimeNotice.aspx,/setApplyOverTime.aspx,"
            + "/setGMDailyStatus.aspx,/syncServiceTechnician.aspx,cipFleet/appFleetHistory.aspx,getOutServiceStation.aspx,updateFuelTripSuggest.aspx,batchBindVehiclesSign.aspx,/cipFleet/updateFleetListByUserId.aspx,/cipFleet/updateFleetList.aspx,/externalDataSynchro/deleteEsnAndFleet.aspx"
            + "/externalDataSynchro/synchroUser.aspx,/externalDataSynchro/synchroEsn.aspx,/externalDataSynchro/verifyPhone.aspx,getVipAttract.aspx,/syncEsnAndUser/syncByUserIdList.aspx,scanCode/saveOrUpdate.aspx,/shanKang/delUser.aspx,/shanKang/updateUser.aspx,"
            + "/shanKang/updateUserWord.aspx,/shanKang/upholdVehicles.aspx,/shanKang/saveUser.aspx,/shanKang/disableOrStartUser.aspx,/externalDataSynchro/receiveLyhUser.aspx,/externalDataSynchro/pushLyhUser.aspx,/externalDataSynchro/pushLyhEsn.aspx,/externalDataSynchro/pushLyhEsnList.aspx,/externalDataSynchro/timingReceiveLyhUser.aspx,/externalDataSynchro/timingReceiveLyhVehicle.aspx,"
            + "/xinGuanJia/getWorkOrderByStationAndEsn.aspx";

    private static final String URI_STR_EXTERNAL = "/externalFleet/upholdUserIdESN.aspx/cipFleet/appGetFleetList.aspx,/cipFleet/appAddFleet.aspx,/cipFleet/checkFleetName.aspx,/cipFleet/appGetEquipmentList.aspx,/cipFleet/appUpdateFleet.aspx,/cipFleet/appDeleteFleet.aspx";

    private final static Log log = LogFactory.getLog(UserLoginIntercept.class);

    @Resource
    private PropertiesUtils propertiesUtils;
    @Autowired
    private RedisClientUtils redisClientUtils;
    @Resource
    private LoginDao loginDao;
    @Resource
    private UserTokenTMapper userTokenTMapper;
    @Resource
    private AKeyLoginUserTokenDao aKeyLoginUserTokenDao;

    @Resource
    private IOutSideTokenService outSideTokenService;

    // 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
    @Pointcut("(execution(* com.cci.kangdao.controller*..*(..))  && !execution(* com.cci.kangdao.controller.AdminController.test(..)))" +
            "|| execution(* com.cci.kangdao.fieldService.controller.*..*(..)) " +
            "|| execution(* com.cci.kangdao.escalation.controller.*..*(..)) " +
            "|| execution(* com.cci.kangdao.wechatOrder.controller.*..*(..)) " +
            "|| execution(* com.cci.kangdao.ecjv.controller.*..*(..)) " +
            "|| execution(* com.cci.kangdao.cipFleet.controller.*..*(..)) " +
            "|| execution(* com.cci.kangdao.externalData.controller.*..*(..)) ")
    public void aspect() {
    }

    @Around("aspect()")
    public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable {
        try {
            Signature signature = pjp.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method targetMethod = methodSignature.getMethod();

            Object[] args = pjp.getArgs();
            HttpServletRequest request = RequestContextHolder.getRequest();
            HttpServletResponse response = RequestContextHolder.getResponse();
            /*for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof HttpServletRequest) {
                    request = (HttpServletRequest) args[i];
                }
                if (args[i] instanceof HttpServletResponse) {
                    response = (HttpServletResponse) args[i];
                }
            }
            if (request == null) { // 第一次登陆不到request
                request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            }*/
            String uri = request.getRequestURI();
            log.info("访问链接：" + uri + "；classname：" + targetMethod.getDeclaringClass().getName() + "；method：" + targetMethod.getName());
            uri = uri.substring(uri.indexOf("/", 2) + 1, uri.length());

            String eAppVersion = request.getHeader("EAppVersion");

            // Token不验证上述接口请求
            if (URI_STR.contains(uri)) {
                return pjp.proceed();
            }
            //外部车队系统默认通过 了
            if (URI_STR_EXTERNAL.contains(uri) && StringUtils.isNotBlank(request.getHeader("Source"))
                    && StringUtils.isNotBlank(request.getHeader("token"))
            ) {
                return pjp.proceed();
            }

            /**
             * 1、首先判断获取header中Authorization 、userid是否存在
             */
            if (StringUtils.isEmpty(request.getHeader("userid"))) {
                return responseError(response, -5);
            }
            if (StringUtils.isEmpty(request.getHeader("Authorization"))) {
                return responseError(response, -5);
            }

            /**
             * 2、判断redis中是否存在userId，如果不存在去数据库中获取
             */
            String userid = request.getHeader("userid");

            userid = AESTool.decryptString(userid);

//            // 当userId不为数值形式时，抛出token错误，触发重新登录
            if (!NumberUtils.isDigits(userid)) {
                log.warn("请求userId非法，抛出token错误,userId：" + userid);
                return responseError(response, -1);
            }
            String token = request.getHeader("Authorization");

            log.info("当前登录用户userid为" + userid + ";eAppVersion为" + eAppVersion);
            String type = "0";   //0:app原生    1:微信公众号      2:本机号码一键登录和手机号验证码一键登录
            if (StringUtils.isNotEmpty(request.getHeader("type"))) {
                type = request.getHeader("type");
            }
            UserT userT = new UserT();
            if ("0".equals(type) || "2".equals(type)) {
                userT = (UserT) redisClientUtils.getObject("userT" + userid);
                if (userT == null) {
                    userT = loginDao.getUserByid(userid);
                    if (userT == null) {
                        return responseError(response, -3);
                    }
                    int redisCacheTime = Integer.valueOf(propertiesUtils.getPropertiesValue("redisCacheTime"));
                    redisClientUtils.setObject("userT" + userT.getId(), userT, redisCacheTime);
                }
                //如果header中没有传type字段   这需要判断出type为几
                if(StringUtils.isEmpty(request.getHeader("type"))){
                    AKeyLoginUserTokenT aKeyLoginUserTokenMsg = aKeyLoginUserTokenDao.getAKeyLoginUserTokenByUserId(userT.getId(),1);
                    if(null != aKeyLoginUserTokenMsg && aKeyLoginUserTokenMsg.getLocalToken().equals(token)){
                        type = "2";
                    }
                }
                /**
                 * 3、判断userT中的token是否正确、判断token是否过期
                 */
                log.info("type字段，type值为"+type);
                if("0".equals(type)){   //原来的ad token校验逻辑
                    UserTokenT cacheUserToken = (UserTokenT) redisClientUtils.getObject(RedisKeys.tokenKey(token));
                    if (null == cacheUserToken) {
                        cacheUserToken = userTokenTMapper.selectByAccessToken(token);
                        if (null == cacheUserToken) {
                            if (redisClientUtils.exists(RedisKeys.refreshingTokenKey(token))) {
                                log.info( "Authorization为"+ token);
                                return responseError(response, -2);
                            }
                            // TODO: 2019/4/4 抛出token错误异常
                            log.info( "Authorization为"+ token);
                            return responseError(response, -1);
                        }
                        int redisCacheTime = Integer.valueOf(propertiesUtils.getPropertiesValue("redisCacheTime"));
                        redisClientUtils.setObject(RedisKeys.tokenKey(token), cacheUserToken, redisCacheTime);
                    }
                    if (!Long.valueOf(userid).equals(cacheUserToken.getUserId())) {
                        log.info( "Authorization为"+ token);
                        return responseError(response, -1);
                    }
                    if (!LocalTimeUtil.isBefore(LocalTimeUtil.getRealCurrentTime(), cacheUserToken.getExpireTime())) {
                        log.info( "Authorization为"+ token);
                        return responseError(response, -2);
                    }
                }else{   //新增local token校验逻辑
                    if(StringUtils.isEmpty(userT.getUserName()) || StringUtils.isEmpty(userT.getPhone())){
                        return responseError(response, -3);
                    }
                    //String aKeyLoginUserTRedisKey = "AKeyLogin-UserT-" + userT.getUserName();
                    //存在a用户的用户名是b用户的手机号，所以需要判断redis是否存在当前用户的用户名和手机号的key是否存在，存在则取出来和前端传来的token比较
                    //用户名的本地token
                    AKeyLoginUserTokenT userNameAKeyLoginUserTokenT = (AKeyLoginUserTokenT)redisClientUtils.getObject(RedisKeys.localTokenKey(userT.getUserName()));
                    //手机号的本地token
                    AKeyLoginUserTokenT phoneAKeyLoginUserTokenT = (AKeyLoginUserTokenT)redisClientUtils.getObject(RedisKeys.localTokenKey(userT.getPhone()));
                    //保存在数据库的本地token（本地token可能有adTokenLogin--------开头）
                    AKeyLoginUserTokenT dbAKeyLoginUserTokenT = null;
                    if (null == userNameAKeyLoginUserTokenT && null == phoneAKeyLoginUserTokenT ) {
                        dbAKeyLoginUserTokenT = aKeyLoginUserTokenDao.getAKeyLoginUserTokenByUserId(userT.getId(),1);
                    }
                    //真正的本地token
                    AKeyLoginUserTokenT relAKeyLoginUserTokenT = null;
                    if(null != userNameAKeyLoginUserTokenT && StringUtils.isNotEmpty(userNameAKeyLoginUserTokenT.getLocalToken()) && userNameAKeyLoginUserTokenT.getLocalToken().equals(token)){
                        relAKeyLoginUserTokenT = userNameAKeyLoginUserTokenT;
                    }
                    if(null != phoneAKeyLoginUserTokenT && StringUtils.isNotEmpty(phoneAKeyLoginUserTokenT.getLocalToken()) && phoneAKeyLoginUserTokenT.getLocalToken().equals(token)){
                        relAKeyLoginUserTokenT = phoneAKeyLoginUserTokenT;
                    }
                    if(null != dbAKeyLoginUserTokenT && StringUtils.isNotEmpty(dbAKeyLoginUserTokenT.getLocalToken()) && dbAKeyLoginUserTokenT.getLocalToken().equals(token)){
                        relAKeyLoginUserTokenT = dbAKeyLoginUserTokenT;
                    }

                    if(null == relAKeyLoginUserTokenT ){
                        log.info( "Authorization为"+ token);
                        return responseError(response, -1);
                    }
                    int redisCacheTime = Integer.valueOf(propertiesUtils.getPropertiesValue("redisAKeyLoginTokenCacheTime"));
                    redisClientUtils.setObject(RedisKeys.localTokenKey(userT.getPhone()), relAKeyLoginUserTokenT, redisCacheTime);

                    if (!Long.valueOf(userid).equals(relAKeyLoginUserTokenT.getUserId())) {
                        log.info( "Authorization为"+ token);
                        return responseError(response, -1);
                    }

                    if(!token.equals(relAKeyLoginUserTokenT.getLocalToken())){
                        log.info( "Authorization为"+ token);
                        return responseError(response, -1);
                    }

                    if (!LocalTimeUtil.isBefore(LocalTimeUtil.getRealCurrentTime(), relAKeyLoginUserTokenT.getExpireTime())) {
                        log.info( "Authorization为"+ token);
                        return responseError(response, -2);
                    }
                }
            } else if ("1".equals(type)) {
                //外部唯一ID
                String outSideId = request.getHeader("outSideId");
                //渠道来源
                String companyId = request.getHeader("companyId");

                if (StringUtils.isEmpty(companyId)) {
                    return responseError(response, -5);
                }
                //获取用户信息
                userT = outSideTokenService.getUserByWeChatToken(Long.parseLong(companyId),Integer.parseInt(type),outSideId);
                if (userT == null) {
                    return responseError(response, -3);
                }
                //保存用户信息
                outSideTokenService.setCacheUser(Long.parseLong(companyId), Integer.parseInt(type), outSideId, userT);
                //获取outSideToken
                UserOutSideTokenT cacheUserToken = outSideTokenService.getOutSideToken(outSideId, token);
                if (null == cacheUserToken) {
                    return responseError(response, -1);
                }
                //保存token
                outSideTokenService.setCacheOutSideToken(outSideId, token, cacheUserToken);
                //判断是否已经过期   通过微信获取的token，不验证失效，不用刷新token
//                if (!LocalTimeUtil.isBefore(LocalTimeUtil.getRealCurrentTime(), cacheUserToken.getExpireTime())) {
//                    return responseError(response, -2);
//                }

            } else {
                log.warn("请求类型非字典项type：" + type);
                return responseError(response, -100);
            }

            UserThreadLocal.set(userT);
            return pjp.proceed();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            ParameterTool.writeErrorResponse(RequestContextHolder.getResponse());
            return null;
        } finally {
            if (null != UserThreadLocal.get()) {
                UserThreadLocal.clear();
            }
            if (null != PageHelper.getLocalPage()) {
                PageHelper.clearPage();
            }
        }
    }

    private Object responseError(HttpServletResponse response, Integer error) {
        try {
            JSONObject obj = new JSONObject();
            obj.put("status", error);
            obj.put("msg", ApiErrorCode.getInstance().getErrorMsg(error));
            //返回客户
            ParameterTool.writeResponse(response, obj.toString());
            obj = null;
        } catch (JSONException e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }
}
